﻿using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using ESRI.ArcGIS.Client.Geometry;

namespace ESRI.ArcGIS.Client.Tasks.Async
{
    public static partial class GeometryServiceExtensions
    {
        #region AreasAndLengths

        /// <summary>
        /// Calculates the areas and lengths of each specified polygon.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The graphics used to calculate areas and lengths.</param>
        /// <returns></returns>
        public static Task<AreasAndLengthsTaskResult> AreasAndLengthsTaskAsync(this GeometryService task,
            IList<Graphic> graphics)
        {
            return AreasAndLengthsTaskAsync(task, graphics, CancellationToken.None);
        }

        /// <summary>
        /// Calculates the areas and lengths of each specified polygon.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The graphics used to calculate areas and lengths.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<AreasAndLengthsTaskResult> AreasAndLengthsTaskAsync(this GeometryService task,
            IList<Graphic> graphics, CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, AreasAndLengthsEventArgs, AreasAndLengthsTaskResult>(
                t => t.AreasAndLengthsAsync(graphics),
                (t, e) => t.AreasAndLengthsCompleted += e,
                (t, e) => t.AreasAndLengthsCompleted -= e,
                e => new AreasAndLengthsTaskResult(e),
                cancellationToken);
        }

        /// <summary>
        /// Calculates the areas and lengths of each specified polygon.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The graphics used to calculate areas and lengths.</param>
        /// <param name="lengthUnit">The length unit in which perimeters of polygons will be calculated.
        /// If unit is not specified, the units are derived from the spatial reference of the first geometry.</param>
        /// <param name="areaUnit">The area unit in which areas of polygons will be calculated.
        /// If unit is not specified, the units are derived from the spatial reference of the first geometry.</param>
        /// <returns></returns>
        public static Task<AreasAndLengthsTaskResult> AreasAndLengthsTaskAsync(this GeometryService task,
            IList<Graphic> graphics, LinearUnit? lengthUnit, LinearUnit? areaUnit)
        {
            return AreasAndLengthsTaskAsync(task, graphics, lengthUnit, areaUnit, CancellationToken.None);
        }

        /// <summary>
        /// Calculates the areas and lengths of each specified polygon.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The graphics used to calculate areas and lengths.</param>
        /// <param name="lengthUnit">The length unit in which perimeters of polygons will be calculated.
        /// If unit is not specified, the units are derived from the spatial reference of the first geometry.</param>
        /// <param name="areaUnit">The area unit in which areas of polygons will be calculated.
        /// If unit is not specified, the units are derived from the spatial reference of the first geometry.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<AreasAndLengthsTaskResult> AreasAndLengthsTaskAsync(this GeometryService task,
            IList<Graphic> graphics, LinearUnit? lengthUnit, LinearUnit? areaUnit,
            CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, AreasAndLengthsEventArgs, AreasAndLengthsTaskResult>(
                t => t.AreasAndLengthsAsync(graphics, lengthUnit, areaUnit, null),
                (t, e) => t.AreasAndLengthsCompleted += e,
                (t, e) => t.AreasAndLengthsCompleted -= e,
                e => new AreasAndLengthsTaskResult(e), 
                cancellationToken);
        }

        #endregion

        #region AutoComplete

        /// <summary>
        /// Executes the Auto Complete geometry service.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="polygons">The collection of polygons that will provide some boundaries for new polygons.
        /// The spatial reference of the polygons is specified by sr.</param>
        /// <param name="polylines">An collection of polylines that will provide the remaining boundaries for new polygons.
        /// The spatial reference of the polylines is specified by sr.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> AutoCompleteTaskAsync(this GeometryService task, IList<Graphic> polygons,
            IList<Graphic> polylines)
        {
            return AutoCompleteTaskAsync(task, polygons, polylines, CancellationToken.None);
        }

        /// <summary>
        /// Executes the Auto Complete geometry service.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="polygons">The collection of polygons that will provide some boundaries for new polygons.
        /// The spatial reference of the polygons is specified by sr.</param>
        /// <param name="polylines">An collection of polylines that will provide the remaining boundaries for new polygons.
        /// The spatial reference of the polylines is specified by sr.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> AutoCompleteTaskAsync(this GeometryService task, IList<Graphic> polygons,
            IList<Graphic> polylines, CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, GraphicsEventArgs, GraphicsTaskResult>(
                t => t.AutoCompleteAsync(polygons, polylines),
                (t, e) => t.AutoCompleteCompleted += e,
                (t, e) => t.AutoCompleteCompleted -= e,
                e => new GraphicsTaskResult(e),
                cancellationToken);
        }

        #endregion

        #region Buffer

        /// <summary>
        /// Buffers a set of geometries.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="bufferParameters">The buffer parameters.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> BufferTaskAsync(this GeometryService task, BufferParameters bufferParameters)
        {
            return BufferTaskAsync(task, bufferParameters, CancellationToken.None);
        }

        /// <summary>
        /// Buffers a set of geometries.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="bufferParameters">The buffer parameters.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> BufferTaskAsync(this GeometryService task, BufferParameters bufferParameters,
            CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, GraphicsEventArgs, GraphicsTaskResult>(
                t => t.BufferAsync(bufferParameters),
                (t, e) => t.BufferCompleted += e,
                (t, e) => t.BufferCompleted -= e,
                e => new GraphicsTaskResult(e),
                cancellationToken);
        }

        #endregion

        #region ConvexHull

        /// <summary>
        /// The convexHull operation is performed on a geometry service resource. It returns the convex hull
        /// of the input geometry. The input geometry can be a point, multipoint, polyline or polygon.
        /// The hull is typically a polygon but can also be a polyline or point in degenerate cases.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The graphics.</param>
        /// <returns></returns>
        public static Task<GeometryTaskResult> ConvexHullTaskAsync(this GeometryService task, IList<Graphic> graphics)
        {
            return ConvexHullTaskAsync(task, graphics, CancellationToken.None);
        }

        /// <summary>
        /// The convexHull operation is performed on a geometry service resource. It returns the convex hull
        /// of the input geometry. The input geometry can be a point, multipoint, polyline or polygon.
        /// The hull is typically a polygon but can also be a polyline or point in degenerate cases.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The graphics.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<GeometryTaskResult> ConvexHullTaskAsync(this GeometryService task, IList<Graphic> graphics,
            CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, GeometryEventArgs, GeometryTaskResult>(
                t => t.ConvexHullAsync(graphics),
                (t, e) => t.ConvexHullCompleted += e,
                (t, e) => t.ConvexHullCompleted -= e,
                e => new GeometryTaskResult(e),
                cancellationToken);
        }

        #endregion

        #region Cut

        /// <summary>
        /// Splits input polylines or polygons where they cross a cutting polyline.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="target">The input polylines or polygons to be cut.</param>
        /// <param name="cutter">The polyline that will be used to divide the targets into pieces where they cross the cutter.</param>
        /// <returns></returns>
        public static Task<CutTaskResult> CutTaskAsync(this GeometryService task, IList<Graphic> target,
            Polyline cutter)
        {
            return CutTaskAsync(task, target, cutter, CancellationToken.None);
        }

        /// <summary>
        /// Splits input polylines or polygons where they cross a cutting polyline.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="target">The input polylines or polygons to be cut.</param>
        /// <param name="cutter">The polyline that will be used to divide the targets into pieces where they cross the cutter.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<CutTaskResult> CutTaskAsync(this GeometryService task, IList<Graphic> target,
            Polyline cutter, CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, CutEventArgs, CutTaskResult>(
                t => t.CutAsync(target, cutter, null),
                (t, e) => t.CutCompleted += e,
                (t, e) => t.CutCompleted -= e,
                e => new CutTaskResult(e), 
                cancellationToken);
        }

        #endregion

        #region Densify

        /// <summary>
        /// This operation densifies geometries by plotting points between existing vertices.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The collection of Geometry to be densified. All geometries are assumed to be
        /// in the coordinate system SpatialReference.</param>
        /// <param name="parameters">Parameters contains additional properties that defines how the densify task
        /// will be preformed.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> DensifyTaskAsync(this GeometryService task, IList<Graphic> graphics,
            DensifyParameters parameters)
        {
            return DensifyTaskAsync(task, graphics, parameters, CancellationToken.None);
        }

        /// <summary>
        /// This operation densifies geometries by plotting points between existing vertices.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The collection of Geometry to be densified. All geometries are assumed to be
        /// in the coordinate system SpatialReference.</param>
        /// <param name="parameters">Parameters contains additional properties that defines how the densify task
        /// will be preformed.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> DensifyTaskAsync(this GeometryService task, IList<Graphic> graphics,
            DensifyParameters parameters, CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, GraphicsEventArgs, GraphicsTaskResult>(
                t => t.DensifyAsync(graphics, parameters),
                (t, e) => t.DensifyCompleted += e,
                (t, e) => t.DensifyCompleted -= e,
                e => new GraphicsTaskResult(e),
                cancellationToken);
        }

        #endregion

        #region Difference

        /// <summary>
        /// Constructs the set-theoretic difference between an array of geometries and another geometry.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="geometries">A collection of points, multipoints, polylines or polygons. The input types can be mixed.</param>
        /// <param name="geometry">A single geometry of any type, of dimension equal to or greater than the elements of the
        /// geometries collection.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> DifferenceTaskAsync(this GeometryService task, IList<Graphic> geometries,
            Geometry.Geometry geometry)
        {
            return DifferenceTaskAsync(task, geometries, geometry, CancellationToken.None);
        }

        /// <summary>
        /// Constructs the set-theoretic difference between an array of geometries and another geometry.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="geometries">A collection of points, multipoints, polylines or polygons. The input types can be mixed.</param>
        /// <param name="geometry">A single geometry of any type, of dimension equal to or greater than the elements of the
        /// geometries collection.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> DifferenceTaskAsync(this GeometryService task, IList<Graphic> geometries,
            Geometry.Geometry geometry, CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, GraphicsEventArgs, GraphicsTaskResult>(
                t => t.DifferenceAsync(geometries, geometry),
                (t, e) => t.DifferenceCompleted += e,
                (t, e) => t.DifferenceCompleted -= e,
                e => new GraphicsTaskResult(e),
                cancellationToken);
        }

        #endregion

        #region Distance

        /// <summary>
        /// Calculates the distance between two geometries.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="geometry1">The geometry from where the distance is to be measured.</param>
        /// <param name="geometry2">The geometry to which the distance is to be measured. The structure of geometry
        /// is same as the structure of the JSON geometry objects returned by the ArcGIS REST API.
        /// The use of simple syntax is not supported.</param>
        /// <param name="parameters">Parameters contains additional properties that defines how the distance task will
        /// be preformed.</param>
        /// <returns></returns>
        public static Task<DistanceTaskResult> DistanceTaskAsync(this GeometryService task, Geometry.Geometry geometry1,
            Geometry.Geometry geometry2, DistanceParameters parameters)
        {
            return DistanceTaskAsync(task, geometry1, geometry2, parameters, CancellationToken.None);
        }

        /// <summary>
        /// Calculates the distance between two geometries.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="geometry1">The geometry from where the distance is to be measured.</param>
        /// <param name="geometry2">The geometry to which the distance is to be measured. The structure of geometry
        /// is same as the structure of the JSON geometry objects returned by the ArcGIS REST API.
        /// The use of simple syntax is not supported.</param>
        /// <param name="parameters">Parameters contains additional properties that defines how the distance task will
        /// be preformed.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<DistanceTaskResult> DistanceTaskAsync(this GeometryService task, Geometry.Geometry geometry1,
            Geometry.Geometry geometry2, DistanceParameters parameters, CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, DistanceEventArgs, DistanceTaskResult>(
                t => t.DistanceAsync(geometry1, geometry2, parameters),
                (t, e) => t.DistanceCompleted += e,
                (t, e) => t.DistanceCompleted -= e,
                e => new DistanceTaskResult(e),
                cancellationToken);
        }

        #endregion

        #region Generalize

        /// <summary>
        /// Applies Douglas-Poiker generalization to the input polylines and polygons.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The collection of polylines and polygons to be generalized.</param>
        /// <param name="parameters">Parameters contains additional properties that defines how the generalize task will
        /// be performed.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> GeneralizeTaskAsync(this GeometryService task, IList<Graphic> graphics,
            GeneralizeParameters parameters)
        {
            return GeneralizeTaskAsync(task, graphics, parameters, CancellationToken.None);
        }

        /// <summary>
        /// Applies Douglas-Poiker generalization to the input polylines and polygons.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The collection of polylines and polygons to be generalized.</param>
        /// <param name="parameters">Parameters contains additional properties that defines how the generalize task will
        /// be preformed.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> GeneralizeTaskAsync(this GeometryService task, IList<Graphic> graphics,
            GeneralizeParameters parameters, CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, GraphicsEventArgs, GraphicsTaskResult>(
                t => t.GeneralizeAsync(graphics, parameters),
                (t, e) => t.GeneralizeCompleted += e,
                (t, e) => t.GeneralizeCompleted -= e,
                e => new GraphicsTaskResult(e),
                cancellationToken);
        }

        #endregion

        #region Intersect

        /// <summary>
        /// Constructs the set-theoretic intersection between an collection of geometries and another geometry.
        /// Only the geometries of the collection that intersect will be returned.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="geometries">A collection of points, multipoints, polylines or polygons. The input types can be mixed.</param>
        /// <param name="geometry">A single geometry of any type.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> IntersectTaskAsync(this GeometryService task, IList<Graphic> geometries,
            Geometry.Geometry geometry)
        {
            return IntersectTaskAsync(task, geometries, geometry, CancellationToken.None);
        }

        /// <summary>
        /// Constructs the set-theoretic intersection between an collection of geometries and another geometry.
        /// Only the geometries of the collection that intersect will be returned.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="geometries">A collection of points, multipoints, polylines or polygons. The input types can be mixed.</param>
        /// <param name="geometry">A single geometry of any type.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> IntersectTaskAsync(this GeometryService task, IList<Graphic> geometries,
            Geometry.Geometry geometry, CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, GraphicsEventArgs, GraphicsTaskResult>(
                t => t.IntersectAsync(geometries, geometry),
                (t, e) => t.IntersectCompleted += e,
                (t, e) => t.IntersectCompleted -= e,
                e => new GraphicsTaskResult(e),
                cancellationToken);
        }

        #endregion

        #region LabelPoints

        /// <summary>
        /// Generates a set of points suitable for displaying labels for the given set of Graphics.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The graphics used to calculate label points.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> LabelPointsTaskAsync(this GeometryService task, IList<Graphic> graphics)
        {
            return LabelPointsTaskAsync(task, graphics, CancellationToken.None);
        }

        /// <summary>
        /// Generates a set of points suitable for displaying labels for the given set of Graphics.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The graphics used to calculate label points.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> LabelPointsTaskAsync(this GeometryService task, IList<Graphic> graphics,
            CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, GraphicsEventArgs, GraphicsTaskResult>(
                t => t.LabelPointsAsync(graphics),
                (t, e) => t.LabelPointsCompleted += e,
                (t, e) => t.LabelPointsCompleted -= e,
                e => new GraphicsTaskResult(e),
                cancellationToken);
        }

        #endregion

        #region Lengths

        /// <summary>
        /// Calculates the lengths of each specified polyline.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The graphics used to calculate lengths.</param>
        /// <returns></returns>
        public static Task<LengthsTaskResult> LengthsTaskAsync(this GeometryService task, IList<Graphic> graphics)
        {
            return LengthsTaskAsync(task, graphics, CancellationToken.None);
        }

        /// <summary>
        /// Calculates the lengths of each specified polyline.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The graphics used to calculate lengths.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<LengthsTaskResult> LengthsTaskAsync(this GeometryService task, IList<Graphic> graphics,
            CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, LengthsEventArgs, LengthsTaskResult>(
                t => t.LengthsAsync(graphics),
                (t, e) => t.LengthsCompleted += e,
                (t, e) => t.LengthsCompleted -= e,
                e => new LengthsTaskResult(e),
                cancellationToken);
        }

        #endregion

        #region Offset

        /// <summary>
        /// Constructs offset copies of the input polylines or polygons.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">A collection of polylines or polygons. Input types can be mixed.
        /// All geometries are assumed to be in the coordinate system defined by the input SpatialReference.</param>
        /// <param name="parameters">Parameters contains additional properties that defines how the offset task will
        /// be performed.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> OffsetTaskAsync(this GeometryService task, IList<Graphic> graphics,
            OffsetParameters parameters)
        {
            return OffsetTaskAsync(task, graphics, parameters, CancellationToken.None);
        }

        /// <summary>
        /// Constructs offset copies of the input polylines or polygons.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">A collection of polylines or polygons. Input types can be mixed.
        /// All geometries are assumed to be in the coordinate system defined by the input SpatialReference.</param>
        /// <param name="parameters">Parameters contains additional properties that defines how the offset task will
        /// be performed.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> OffsetTaskAsync(this GeometryService task, IList<Graphic> graphics,
            OffsetParameters parameters, CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, GraphicsEventArgs, GraphicsTaskResult>(
                t => t.OffsetAsync(graphics, parameters),
                (t, e) => t.OffsetCompleted += e,
                (t, e) => t.OffsetCompleted -= e,
                e => new GraphicsTaskResult(e),
                cancellationToken);
        }

        #endregion

        #region Project

        /// <summary>
        /// Projects a set of geometries into a new spatial reference.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The graphics containing the geometry to project.</param>
        /// <param name="outSpatialReference">The SpatialReference to project to.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> ProjectTaskAsync(this GeometryService task, IList<Graphic> graphics,
            SpatialReference outSpatialReference)
        {
            return ProjectTaskAsync(task, graphics, outSpatialReference, CancellationToken.None);
        }

        /// <summary>
        /// Projects a set of geometries into a new spatial reference.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The graphics containing the geometry to project.</param>
        /// <param name="outSpatialReference">The SpatialReference to project to.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> ProjectTaskAsync(this GeometryService task, IList<Graphic> graphics,
            SpatialReference outSpatialReference, CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, GraphicsEventArgs, GraphicsTaskResult>(
                t => t.ProjectAsync(graphics, outSpatialReference),
                (t, e) => t.ProjectCompleted += e,
                (t, e) => t.ProjectCompleted -= e,
                e => new GraphicsTaskResult(e),
                cancellationToken);
        }

        #endregion

        #region Relation

        /// <summary>
        /// Computes the set of pairs of geometries from graphics1 and graphics2 that belong to the specified relation.
        /// Both arrays are assumed to be in the same spatial reference. The relations are evaluated in 2D.
        /// Z coordinates are not used. Geometry types cannot be mixed within an array. Note that if the spatialRelationship
        /// is specified as esriGeometryRelationRelation, the relationParam parameter describes the spatial relationship
        /// and must be specified.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics1">The first set of graphics in the comparison.</param>
        /// <param name="graphics2">The second set of graphics in the comparison.</param>
        /// <param name="spatialRelationship">The spatial relationship to be tested between the two sets of geometries.
        /// If GeometryRelation.esriGeometryRelationRelation is specified, then the comparisonString parameter must
        /// be specified.</param>
        /// <param name="comparisonString">The 'Shape Comparison Language' string to be evaluated. Strings such as
        /// RELATE(G1, G2, "FFFTTT***") are accepted, in addition to other kinds of strings. This string describes the
        /// spatial relationship to be tested when the spatialRelationship parameter is specified as
        /// esriGeometryRelationRelation.</param>
        /// <returns></returns>
        public static Task<RelationTaskResult> RelationTaskAsync(this GeometryService task, IList<Graphic> graphics1,
            IList<Graphic> graphics2, GeometryRelation spatialRelationship,
            string comparisonString)
        {
            return RelationTaskAsync(task, graphics1, graphics2, spatialRelationship, comparisonString, CancellationToken.None);
        }

        /// <summary>
        /// Computes the set of pairs of geometries from graphics1 and graphics2 that belong to the specified relation.
        /// Both arrays are assumed to be in the same spatial reference. The relations are evaluated in 2D.
        /// Z coordinates are not used. Geometry types cannot be mixed within an array. Note that if the spatialRelationship
        /// is specified as esriGeometryRelationRelation, the relationParam parameter describes the spatial relationship
        /// and must be specified.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics1">The first set of graphics in the comparison.</param>
        /// <param name="graphics2">The second set of graphics in the comparison.</param>
        /// <param name="spatialRelationship">The spatial relationship to be tested between the two sets of geometries.
        /// If GeometryRelation.esriGeometryRelationRelation is specified, then the comparisonString parameter must
        /// be specified.</param>
        /// <param name="comparisonString">The 'Shape Comparison Language' string to be evaluated. Strings such as
        /// RELATE(G1, G2, "FFFTTT***") are accepted, in addition to other kinds of strings. This string describes the
        /// spatial relationship to be tested when the spatialRelationship parameter is specified as
        /// esriGeometryRelationRelation.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<RelationTaskResult> RelationTaskAsync(this GeometryService task, IList<Graphic> graphics1,
            IList<Graphic> graphics2, GeometryRelation spatialRelationship,
            string comparisonString, CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, RelationEventArgs, RelationTaskResult>(
                t => t.RelationAsync(graphics1, graphics2, spatialRelationship, comparisonString),
                (t, e) => t.RelationCompleted += e,
                (t, e) => t.RelationCompleted -= e,
                e => new RelationTaskResult(e),
                cancellationToken);
        }

        #endregion

        #region Reshape

        /// <summary>
        /// Reshapes a polyline or a part of a polygon using a reshaping line.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="target">Polygon or Polyline to be reshaped.</param>
        /// <param name="reshaper">The reshaper.</param>
        /// <returns></returns>
        public static Task<GeometryTaskResult> ReshapeTaskAsync(this GeometryService task, Geometry.Geometry target, Polyline reshaper)
        {
            return ReshapeTaskAsync(task, target, reshaper, CancellationToken.None);
        }

        /// <summary>
        /// Reshapes a polyline or a part of a polygon using a reshaping line.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="target">Polygon or Polyline to be reshaped.</param>
        /// <param name="reshaper">The reshaper.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<GeometryTaskResult> ReshapeTaskAsync(this GeometryService task, Geometry.Geometry target, Polyline reshaper,
            CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, GeometryEventArgs, GeometryTaskResult>(
                t => t.ReshapeAsync(target, reshaper),
                (t, e) => t.ReshapeCompleted += e,
                (t, e) => t.ReshapeCompleted -= e,
                e => new GeometryTaskResult(e),
                cancellationToken);
        }

        #endregion

        #region Simplify

        /// <summary>
        /// Alters the given geometries to make their definitions topologically legal with respect to their geometry type.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The graphics containing the geometry to simplify.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> SimplifyTaskAsync(this GeometryService task, IList<Graphic> graphics)
        {
            return SimplifyTaskAsync(task, graphics, CancellationToken.None);
        }

        /// <summary>
        /// Alters the given geometries to make their definitions topologically legal with respect to their geometry type.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="graphics">The graphics containing the geometry to simplify.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> SimplifyTaskAsync(this GeometryService task, IList<Graphic> graphics,
            CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, GraphicsEventArgs, GraphicsTaskResult>(
                t => t.SimplifyAsync(graphics),
                (t, e) => t.SimplifyCompleted += e,
                (t, e) => t.SimplifyCompleted -= e,
                e => new GraphicsTaskResult(e),
                cancellationToken);
        }

        #endregion

        #region TrimExtend

        /// <summary>
        /// Trims or extends each input polyline to meet another polyline.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="polylines">The collection of polylines to be trimmed or extended.</param>
        /// <param name="trimExtendTo">The defining trim/extend line.</param>
        /// <param name="extendHow">Flags controlling the extend operation.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> TrimExtendTaskAsync(this GeometryService task, IList<Polyline> polylines,
            Polyline trimExtendTo, CurveExtension extendHow)
        {
            return TrimExtendTaskAsync(task, polylines, trimExtendTo, extendHow, CancellationToken.None);
        }

        /// <summary>
        /// Trims or extends each input polyline to meet another polyline.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="polylines">The collection of polylines to be trimmed or extended.</param>
        /// <param name="trimExtendTo">The defining trim/extend line.</param>
        /// <param name="extendHow">Flags controlling the extend operation.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<GraphicsTaskResult> TrimExtendTaskAsync(this GeometryService task, IList<Polyline> polylines,
            Polyline trimExtendTo, CurveExtension extendHow, CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, GraphicsEventArgs, GraphicsTaskResult>(
                t => t.TrimExtendAsync(polylines, trimExtendTo, extendHow),
                (t, e) => t.TrimExtendCompleted += e,
                (t, e) => t.TrimExtendCompleted -= e,
                e => new GraphicsTaskResult(e),
                cancellationToken);
        }

        #endregion

        #region Union

        /// <summary>
        /// This operation constructs the set-theoretic union of the geometries in the input collection.
        /// All inputs must be of the same type.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="geometries">A collection of points, multipoints, polylines or polygons.
        /// The input types cannot be mixed.</param>
        /// <returns></returns>
        public static Task<GeometryTaskResult> UnionTaskAsync(this GeometryService task, IList<Graphic> geometries)
        {
            return UnionTaskAsync(task, geometries, CancellationToken.None);
        }

        /// <summary>
        /// This operation constructs the set-theoretic union of the geometries in the input collection.
        /// All inputs must be of the same type.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="geometries">A collection of points, multipoints, polylines or polygons.
        /// The input types cannot be mixed.</param>
        /// <param name="cancellationToken">Cancellation token for cancelling task.</param>
        /// <returns></returns>
        public static Task<GeometryTaskResult> UnionTaskAsync(this GeometryService task, IList<Graphic> geometries,
            CancellationToken cancellationToken)
        {
            return task.HandleTask<GeometryService, GeometryEventArgs, GeometryTaskResult>(
                t => t.UnionAsync(geometries),
                (t, e) => t.UnionCompleted += e,
                (t, e) => t.UnionCompleted -= e,
                e => new GeometryTaskResult(e),
                cancellationToken);
        }

        #endregion

    }
}
